package org.gitman.agentplatform.core;

import java.awt.Color;
import java.io.File;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.gitman.agentplatform.agent.api.Agent;
import org.gitman.agentplatform.agent.api.AgentState;
import org.gitman.agentplatform.agent.api.InternalAgentInfo;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class PersistenceManager {
	private static PersistenceManager instance;
	private static final String SIMULATION = "simulation";
	private static final String PARAMS = "parameters";
	private static final String AGENTS = "agents";
	private static final String AGENT = "agent";
	private static final String PREDICT = "predictability";
	private static final String OBSERV = "observability";
	private static final String RELIE = "relieability";
	private static final String MIN = "min";
	private static final String MAX = "max";
	private static final String VAL = "val";
	private static final String USE_DEFAULT = "useDefault";
	private static final String VERTICES = "vertices";
	private static final String START = "start";
	private static final String END = "end";
	private static final String KNOWN = "known";
	private static final String ARGS = "args";
	private static final String COLOR = "color";
	private static final String ID = "id";
	private static final String CLASS = "class";
	
	private PersistenceManager(){
	}
	public static PersistenceManager getInstance(){
		if(instance ==null){
			instance = new PersistenceManager();
		}
		return instance;
	}
	
	public void saveSimulation2Xml(File f){
		AgentManager agentManager = PlatformManager.getInstatnce().getSimulation().getAgentManager();
		try {
			DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();			
			DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
			
			Document doc = docBuilder.newDocument();
			Element simElement = doc.createElement(SIMULATION);
			doc.appendChild(simElement);
			
			Element paramElement = doc.createElement(PARAMS);
			simElement.appendChild(paramElement);
			
			Element predictElement = doc.createElement(PREDICT);
			predictElement.setAttribute(USE_DEFAULT, agentManager.isUseDefPredict()+"");
			predictElement.setAttribute(MIN, agentManager.getDefaultMinPredict().toString());
			predictElement.setAttribute(MAX, agentManager.getDefaultMaxPredict().toString());			
			paramElement.appendChild(predictElement);
			
			Element observElement = doc.createElement(OBSERV);
			observElement.setAttribute(USE_DEFAULT, agentManager.isUseDefObserv()+"");
			observElement.setAttribute(MIN, agentManager.getDefaultMinObserv().toString());
			observElement.setAttribute(MAX, agentManager.getDefaultMaxObserv().toString());			
			paramElement.appendChild(observElement);
			
			Element relieElement = doc.createElement(RELIE);
			relieElement.setAttribute(USE_DEFAULT, agentManager.isUseDefReliab()+"");
			relieElement.setAttribute(VAL, agentManager.getDefaultReliab().toString());	
			paramElement.appendChild(relieElement);			
			
			Element agentsElement = doc.createElement(AGENTS);
			simElement.appendChild(agentsElement);
			
			for(Agent agent : agentManager.getAgents()){
				InternalAgentInfo info = agentManager.getAgentInfo(agent);
				Element agentElement = doc.createElement(AGENT);
				agentElement.setAttribute(ID, info.getId().toString());
				agentElement.setAttribute(CLASS, agent.getClass().getSimpleName());
				agentElement.setAttribute(COLOR, info.getColor().getRGB() + "");
				agentsElement.appendChild(agentElement);
				
				Element verticesElement = doc.createElement(VERTICES);
				verticesElement.setAttribute(START, info.getStartVertexID().toString());
				verticesElement.setAttribute(END, info.getEndVertexID().toString());
				agentElement.appendChild(verticesElement);
				
				Element argsElement = doc.createElement(ARGS);
				argsElement.appendChild(doc.createTextNode(info.getArgs() == null? "" : info.getArgs()));
				agentElement.appendChild(argsElement);
				
				predictElement = doc.createElement(PREDICT);
				predictElement.setAttribute(MIN, info.getMinPredict().toString());
				predictElement.setAttribute(MAX, info.getMaxPredict().toString());
				agentElement.appendChild(predictElement);
				
				observElement = doc.createElement(OBSERV);
				observElement.setAttribute(MIN, info.getMinObserv().toString());
				observElement.setAttribute(MAX, info.getMaxObserv().toString());
				agentElement.appendChild(observElement);
				
				relieElement = doc.createElement(RELIE);
				relieElement.setAttribute(VAL, info.getInternalRelie().toString());
				relieElement.setAttribute(KNOWN, info.isReliKnown()+"");
				agentElement.appendChild(relieElement);			
			}		
			
			
			TransformerFactory transformerFactory = TransformerFactory.newInstance();
			Transformer transformer = transformerFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
			DOMSource source = new DOMSource(doc);
			//StreamResult result = new StreamResult(System.out);
			StreamResult result = new StreamResult(f);
			
			transformer.transform(source, result);
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerConfigurationException e) {
			e.printStackTrace();
		} catch (TransformerException e) {
			e.printStackTrace();
		}		
	}

	public void loadSimulationFromXml(File f){
		try {
			PlatformManager platformManager = PlatformManager.getInstatnce();
			AgentManager agentManager = platformManager.getSimulation().getAgentManager();
			DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder dBuilder;		
			dBuilder = dbFactory.newDocumentBuilder();
			Document doc = dBuilder.parse(f);
			
			doc.getDocumentElement().normalize();
			
			Element params = (Element) doc.getElementsByTagName(PARAMS).item(0);
			Element predict = (Element) params.getElementsByTagName(PREDICT).item(0);
			agentManager.setDefaultMaxObserv(Integer.parseInt(predict.getAttribute(MAX)));
			agentManager.setDefaultMinPredict(Integer.parseInt(predict.getAttribute(MIN)));
			agentManager.setUseDefPredict(Boolean.parseBoolean(predict.getAttribute(USE_DEFAULT)));
			
			Element observ = (Element) params.getElementsByTagName(OBSERV).item(0);
			agentManager.setDefaultMaxObserv(Integer.parseInt(observ.getAttribute(MAX)));
			agentManager.setDefaultMinObserv(Integer.parseInt(observ.getAttribute(MIN)));
			agentManager.setUseDefObserv(Boolean.parseBoolean(observ.getAttribute(USE_DEFAULT)));
			
			Element relie = (Element) params.getElementsByTagName(RELIE).item(0);
			agentManager.setDefaultReliab(Double.parseDouble(relie.getAttribute(VAL)));
			agentManager.setUseDefReliab(Boolean.parseBoolean(relie.getAttribute(USE_DEFAULT)));
			
			Element agents = (Element) doc.getElementsByTagName(AGENTS).item(0);
			NodeList agentList = agents.getElementsByTagName(AGENT);
			for(int i =0;i<agentList.getLength();i++){
				Element agent = (Element) agentList.item(i);
				InternalAgentInfo info = new InternalAgentInfo();
				String args = null;
				Class<? extends Agent> clazz = platformManager.getClassBySimpleName(agent.getAttribute(CLASS));
				info.setId(Integer.parseInt(agent.getAttribute(ID)));
				info.setColor(new Color(Integer.parseInt(agent.getAttribute(COLOR))));
				
				Element vertices = (Element) agent.getElementsByTagName(VERTICES).item(0);
				info.setStartVertexID(Integer.parseInt(vertices.getAttribute(START)));
				info.setEndVertexID(Integer.parseInt(vertices.getAttribute(END)));
				
				args = agent.getElementsByTagName(ARGS).item(0).getTextContent();
				
				predict = (Element) agent.getElementsByTagName(PREDICT).item(0);
				observ = (Element) agent.getElementsByTagName(OBSERV).item(0);
				relie = (Element) agent.getElementsByTagName(RELIE).item(0);
				
				info.setPredict(Integer.parseInt(predict.getAttribute(MIN)), Integer.parseInt(predict.getAttribute(MAX)));
				info.setObserv(Integer.parseInt(observ.getAttribute(MIN)), Integer.parseInt(observ.getAttribute(MAX)));
				info.setRelie(Double.parseDouble(relie.getAttribute(VAL)));
				info.setRelieKnown(Boolean.parseBoolean(relie.getAttribute(KNOWN)));
				info.setAgentState(AgentState.fresh);
				
				agentManager.addAgent(info, clazz, args);
			}			
		} catch (ParserConfigurationException e) {
			e.printStackTrace();
		} catch (SAXException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
